/**************************************************
 * Project Name         [JavaEE_XML]
 * File Name            [SAXParseXML.java]
 * Creation Date        [08-18-2014]
 * 
 * Copyright© ge.y.yang@gmail.com All Rights Reserved
 * 
 * Work hard, play harder, think big and keep fit
 **************************************************/
package pkg_02_sax;

import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;

import pojo.Student;

/**
 * 使用SAX处理XML文档<br/>
 * 
 * SAX是Simple API for XML的缩写, 与DOM比较而言, SAX是一种轻量型的方法<br/>
 * 在处理DOM的时候需要读入整个的XML文档, 然后在内存中创建DOM树, 生成DOM树上的每个Node对象<br/>
 * 大文档处理DOM对于内存的需求很大, 所以在某些应用中使用DOM是一件很不划算的事<br/>
 * 
 * SAX在概念上与DOM完全不同<br/>
 * 首先, 不同于DOM的文档驱动, 它是事件驱动的, 也就是说它并不需要读入整个文档, 而文档的读入过程也就是SAX的解析过程<br/>
 * 所谓事件驱动, 是指一种基于回调(callback)机制的程序运行方法
 * 
 * @author 不落的太阳(Sean Yang)
 * @version 1.0
 * @since JDK 6
 * 
 */
public class SAXParseXML {
	/**
	 * SAX的事件监听器<br>
	 * 当处理特定的XML文件的时候, 就需要为其创建一个实现了ContentHandler的类来处理特定的事件, 其为SAX处理XML文件的核心
	 */
	static class StudentSAXHandler extends DefaultHandler {
		static int counter = 1;
		// 保存已经读到但是还没关闭的标签
		Stack<String> tagStack = new Stack<String>();

		List<Student> students = new ArrayList<Student>();
		Student student = null;

		/**
		 * 当开始解析文档时调用, 可以在其中做一些预处理的工作
		 */
		@Override
		public void startDocument() throws SAXException {
			System.out.println("==========Begin Parser==========");
		}

		/**
		 * 当遇到一个开始标签时调用
		 * 
		 * @param uri
		 *            命名域
		 * @param localName
		 *            标签名
		 * @param qName
		 *            标签的修饰前缀
		 * @param attributes
		 *            这个标签所包含的属性列表,通过atts, 可以得到所有的属性名和相应的值. <name="">
		 */
		@Override
		public void startElement(String uri, String localName, String qName,
				Attributes attributes) throws SAXException {
			tagStack.push(qName);

			if (student == null) {
				if ("STUDENT".equals(qName)) {
					System.out.println("Getting student " + counter
							+ " start......");
					student = new Student();
					for (int i = 0; attributes != null // 注意这里的写法
							&& i < attributes.getLength(); i++) {
						String attrName = attributes.getQName(i);
						String attrValue = attributes.getValue(i);
						System.out.println(attrName + "=" + attrValue);
					}
				}
			}

		}

		/**
		 * 处理XML标签中的内容
		 */
		@Override
		public void characters(char[] ch, int start, int length)
				throws SAXException {
			String tagName = tagStack.peek();
			String value = new String(ch, start, length);
			if ("NAME".equals(tagName)) {
				student.setName(value);
			} else if ("AGE".equals(tagName)) {
				student.setAge(Integer.parseInt(value));
			} else if ("GENDER".equals(tagName)) {
				student.setGender(value);
			} else if ("BIRTHDAY".equals(tagName)) {
				try {
					student.setBirthday(new SimpleDateFormat("yyyy-MM-dd")
							.parse(value));
				} catch (ParseException e) {
					student.setBirthday(new java.util.Date());
					e.printStackTrace();
				}
			} else if ("PHONENUMBER".equals(tagName)) {
				student.setPhoneNumber(value);
			}
		}

		/**
		 * 当遇到一个结束标签时调用
		 * 
		 * @param uri
		 *            命名域
		 * @param localName
		 *            标签名
		 * @param qName
		 *            标签的修饰前缀
		 */
		@Override
		public void endElement(String uri, String localName, String qName)
				throws SAXException {
			String currentTag = tagStack.pop();
			// 最近读到的标签应该与即将关闭的标签一样
			if (!currentTag.equals(qName)) {
				throw new SAXException("XML tag not match......");
			}
			// 如果关闭的是"STUDENT"标签, 则表示一个Student已经构造完毕了. 
			if ("STUDENT".equals(qName)) {
				// 将bean实例放入学生列表中, 同时置空, 等待构造下一个实例
				students.add(student);
				student = null;
				System.out.println("Getting student " + counter + " end......");
				counter++;
			}
		}

		/**
		 * 当文档解析结束时调用, 可以在其中做一些善后工作
		 */
		@Override
		public void endDocument() throws SAXException {
			System.out.println("==========End Parser==========");
		}

		public List<Student> getResult() {
			return students;
		}
	}

	public static List<Student> parseXML(String fileName)
			throws ParserConfigurationException, SAXException, IOException {
		// 创建SAX解析器工厂
		SAXParserFactory factory = SAXParserFactory.newInstance();
		// 得到解析器
		SAXParser parser = factory.newSAXParser();
		// 得到读取器
		XMLReader reader = parser.getXMLReader();
		// 设置事件处理器
		StudentSAXHandler handler = new StudentSAXHandler();
		reader.setContentHandler(handler);

		reader.parse(fileName);

		return handler.getResult();
	}

	public static void main(String[] args) throws ParserConfigurationException,
			SAXException, IOException {
		List<Student> students = parseXML("src/xml/Student.xml");
		System.out.println(students);
	}

}
